/** File name: cellNetwork.cu **/

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <cuda.h>
#include "cellNetwork.h"
#include <Xlib.h>
#include "NetworkFed/dataNetwork.cu"
#include "network.cu"
#include "view.h"

#define WINDOW_TITLE "Cellular Automata with CUDA"
#define NUMBER_CYCLE 10

const int nbCells = sizeof(Cells)/sizeof(CellArray);
//If the number of cells between network and data are different, chosing the smaller one 
int dataSize = ((nbCells > NODE_NUMBER) ? NODE_NUMBER : nbCells);


int main()
{
	int plotSize, NSize, blockSize, nodeSize, channelSize, tabSize, bufferSize, nodeStateSize;
	int *now_h, *next_h, *now_d, *next_d;
	//bool *changed_h, *changed_d;
	canaux *channels_d;
	Colormap colormap;
	TabNeighbours *tabs_h, *tabs_d;
	Buffer *buffer_h, *buffer_d; 
	NodeState *nowState_h, *nowState_d, *nextState_h, *nextState_d, *buffState_d;

	plotSize = 1;
	NSize = 256;
	blockSize = 512;
	nodeSize = NODE_NUMBER * sizeof(int);
	channelSize = NODE_NUMBER * sizeof(canaux);	
	tabSize = sizeof(TabNeighbours) * NODE_NUMBER;
	bufferSize = sizeof(Buffer) * NODE_NUMBER;
	nodeStateSize = NODE_NUMBER * sizeof(NodeState);

	Display *dsp = XOpenDisplay(NULL);	
	if (!dsp){ return 1;}
	colormap = DefaultColormap(dsp, 0);

	int screenNumber = DefaultScreen(dsp);
	unsigned long white = WhitePixel(dsp, screenNumber);
	unsigned long black = BlackPixel(dsp, screenNumber);	

	Window win = XCreateSimpleWindow(dsp,
				DefaultRootWindow(dsp),
				50, 50,// origin
				NSize*plotSize*3, NSize*plotSize*3,// size
				0, black,//border
				white//background
	);

	XStoreName(dsp, win, WINDOW_TITLE);
	XMapWindow(dsp, win);
	
	long eventMask = StructureNotifyMask;
	XSelectInput(dsp, win, eventMask);
	XEvent evt;
	do
	{
		XNextEvent(dsp, &evt);
	}while(evt.type != MapNotify);	

	GC gc = XCreateGC(dsp, win, 0, NULL);	
	
	/***********/
	//Allocating memory to host variables
	tabs_h = (TabNeighbours*) malloc(tabSize);
	buffer_h = (Buffer*) malloc(bufferSize);
	now_h = (int*)malloc(nodeSize);
	next_h = (int*)malloc(nodeSize);
	nowState_h = (NodeState*) malloc(nodeStateSize);
	nextState_h = (NodeState*) malloc(nodeStateSize);
	
	//Allocating memory to device variable
	cudaMalloc((TabNeighbours**) &tabs_d, tabSize);
	cudaMalloc((Buffer**) &buffer_d, bufferSize);
	cudaMalloc((void**)&now_d, nodeSize);
	cudaMalloc((void**)&next_d, nodeSize);
	cudaMalloc((canaux**)&channels_d, channelSize);
	cudaMalloc((NodeState**)&nowState_d, nodeStateSize);
	cudaMalloc((NodeState**)&nextState_d, nodeStateSize);
	cudaMalloc((NodeState**)&buffState_d, nodeStateSize);

	
	//Copying data from host to device
	cudaMemcpy(now_d, now_h, nodeSize, cudaMemcpyHostToDevice);
	cudaMemcpy(channels_d, channels_h, channelSize, cudaMemcpyHostToDevice);

	/*****************Routing************************/
	//Initilizing state of the nodes
	initNodeState(now_h, next_h, NODE_NUMBER);

	showCells(Cells, dsp, colormap, win, gc, plotSize, now_h, NODE_NUMBER);

	//Initializing
	Init<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(tabs_d, buffer_d, channels_d, NODE_NUMBER);
	//Loop
	printf("\nStating distance calculation...\n");
	for (int i = 0; i < NODE_NUMBER; i++)
	{
	    Distances<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(tabs_d, buffer_d, channels_d, NODE_NUMBER, i);
	    cudaThreadSynchronize();
	}
	//Getting maxLoc	
	//GettingMaxLoc<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(tabs_d, buffer_d, NODE_NUMBER);
	/*
	//Computing MaxGlobal
	for (int i = 0; i < NODE_NUMBER; i++)
	{
	    ComputeMaxGlobal<<<(NODE_NUMBER / blockSize) + 1, blockSize>>>(buffer_d, channels_d, NODE_NUMBER);
	    cudaThreadSynchronize();
	}
	*/

	//Copying values from device to host
	cudaMemcpy(tabs_h, tabs_d, tabSize, cudaMemcpyDeviceToHost);
	cudaMemcpy(buffer_h, buffer_d, bufferSize, cudaMemcpyDeviceToHost);
	
	//DumpTab(tabs_h, NODE_NUMBER);

	//DumpMaxLoc(buffer_h, NODE_NUMBER);
	
	/************************Writing Dot file*********************************/

	//WriteNetwork("network.dot", channels_h, NODE_NUMBER);
	//WriteRouteTable("routeTable.dot", tabs_h[6], channels_h);
	//WriteMaxRoutes("maxroute.dot", tabs_h[6], channels_h);
	
	/*************************Routing Packet********************************/
	printf("\nClick on window to continue!\n");
	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	Packet *packets_h;
	int pac_num = 2;
	PacketType type;
	type.file = 1000;
	FileOfId *routes_h;	

	packets_h = (Packet*) malloc(sizeof(Packet) * pac_num);
	routes_h = (FileOfId*) malloc(sizeof(FileOfId) * pac_num);

	InitPacket(type, packets_h, pac_num, NODE_NUMBER);

	RoutingPacket(packets_h[0], tabs_h, channels_h, routes_h[0]);
	//WriteDotFile("RoutingPacket.dot", route);

 	//DumpFile(route);
	for (int i = routes_h[0].limit - 1; i >= 0 ; i=i-2)
	{
	     //now_h[route.newFound[i]] = 0;
	     showCell(Cells[routes_h[0].newFound[i]], dsp, colormap, win, gc, plotSize, black);
	     if (i < routes_h[0].limit - 1)
	     {
	     	showCell(Cells[routes_h[0].newFound[i+2]], dsp, colormap, win, gc, plotSize, 200*200*200);
	     }
	     if (i < routes_h[0].limit - 3)
	     {
	     	showCell(Cells[routes_h[0].newFound[i+4]], dsp, colormap, win, gc, plotSize, -1);
	     }
	}

	printf("\nClick on window to exit!\n");

	eventMask = ButtonPressMask | ButtonReleaseMask;

	XSelectInput(dsp, win, eventMask);
	do{
		XNextEvent(dsp, &evt);
	}while(evt.type != ButtonRelease);

	//Releasing resources
	XDestroyWindow(dsp, win);
	XCloseDisplay(dsp);
		
	cudaFree(tabs_d);
	cudaFree(buffer_d);	
	cudaFree(now_d);
	cudaFree(next_d);
	cudaFree(channels_d);
	cudaFree(nowState_d);
	cudaFree(nextState_d);
	cudaFree(buffState_d);

	free(packets_h);
	free(routes_h);
	free(now_h);
	free(next_h);
	free(tabs_h);
	free(buffer_h);
	free(nowState_h);
	free(nextState_h);
	return 0;
}
